using System;
using System.Collections.Generic;
using Apache.Cassandra;
using Cassandraemon.Operators;
using Cassandraemon.FullText;


namespace Cassandraemon.Builder
{


	class CassandraOperatorCondition<T>  
	{
		private CassandraOperatorType operatorType = CassandraOperatorType.None;
		private Func<object, object> postProcessor;
		
		private byte[] key;
		private string columnFamily;
		private byte[] superColumn;
		private byte[] column;
		
		private List<byte[]> keys;
		private List<byte[]> columns;
		
		private IAnalyzer analyzer;
		private List<string> matches;
		private List<string> unmatches;
		
		private byte[] startKey = new byte[]{};
		private byte[] finishKey = new byte[]{};
		private string startToken;
		private string finishToken;
		private byte[] startColumn = new byte[]{};		// For Column and SuperColum
		private byte[] finishColumn = new byte[]{};		// For Column and SuperColum
		
		private Dictionary<byte[], byte[]> eqIndexExpression  = new Dictionary<byte[], byte[]>(new ByteArrayEqualityComparer());
		private Dictionary<byte[], byte[]> gteIndexExpression = new Dictionary<byte[], byte[]>(new ByteArrayEqualityComparer());
		private Dictionary<byte[], byte[]> gtIndexExpression  = new Dictionary<byte[], byte[]>(new ByteArrayEqualityComparer());
		private Dictionary<byte[], byte[]> lteIndexExpression = new Dictionary<byte[], byte[]>(new ByteArrayEqualityComparer());
		private Dictionary<byte[], byte[]> ltIndexExpression  = new Dictionary<byte[], byte[]>(new ByteArrayEqualityComparer());
		
		private bool reverse = false;
		private int keyCount = 100;
		private int count = 100;
		
		private ConsistencyLevel consistencyLevel;
		
		public bool IsSetOperatorType { get; private set; }
		public bool IsSetPostProcessor { get; private set; }
		public bool IsSetKey { get; private set; }
		public bool IsSetColumnFamily { get; private set; }
		public bool IsSetSuperColumn { get; private set; }
		public bool IsSetColumn { get; private set; }
		public bool IsSetKeys { get; private set; }
		public bool IsSetColumns { get; private set; }
		public bool IsSetAnalyzer { get; private set; }
		public bool IsSetMatches { get; private set; }
		public bool IsSetUnMatches{ get; private set; }
		public bool IsSetStartKey { get; private set; }
		public bool IsSetFinishKey { get; private set; }
		public bool IsSetStartToken { get; private set; }
		public bool IsSetFinishToken { get; private set; }
		public bool IsSetStartColumn { get; private set; }
		public bool IsSetFinishColumn { get; private set; }
		public bool IsSetReverse { get; private set; }
		public bool IsSetKeyCount { get; private set; }
		public bool IsSetCount { get; private set; }
		public bool IsSetConsistencyLevel { get; private set; }
		
		public CassandraOperatorType OperatorType 
		{
			get
			{
				return operatorType;
			}
			set 
			{
				operatorType = value;
				IsSetOperatorType = true;
			}
		}
		
		public Func<object, object> PostProcessor
		{
			get
			{
				return postProcessor;
			}
			set 
			{
				postProcessor = value;
				IsSetPostProcessor = true;
			}
		}
		
		public byte[] Key 
		{
			get
			{
				return key;
			}
			set
			{
				key = value;
				IsSetKey = true;
			}
		}
		
		public string ColumnFamily {
			get
			{
				return columnFamily;
			}
			set
			{
				columnFamily = value;
				IsSetColumnFamily = true;
			}
		}
		
		public byte[] SuperColumn {
			get
			{
				return superColumn;
			}
			set
			{
				superColumn = value;
				IsSetSuperColumn = true;
			}
		}
		
		public byte[] Column {
			get
			{
				return column;
			}
			set
			{
				column = value;
				IsSetColumn = true;
			}
		}
		
		public List<byte[]> Keys 
		{
			get
			{
				return keys;
			}
			set
			{
				keys = value;
				IsSetKeys = true;
			}
		}
		
		public List<byte[]> Columns
		{
			get
			{
				return columns;
			}
			set
			{
				columns = value;
				IsSetColumns = true;
			}
		}
		
		public IAnalyzer Analyzer
		{
			get
			{
				return analyzer;
			}
			set
			{
				analyzer = value;
				IsSetAnalyzer = true;
			}
		}
		
		public List<string> Matches
		{
			get
			{
				return matches;
			}
			set
			{
				matches = value;
				IsSetMatches = true;
			}
		}
		
		public List<string> UnMatches
		{
			get
			{
				return unmatches;
			}
			set
			{
				unmatches = value;
				IsSetUnMatches = true;
			}
		}
		
		public byte[] StartKey {
			get
			{
				return startKey;
			}
			set
			{
				startKey = value;
				IsSetStartKey = true;
			}
		}
		
		public byte[] FinishKey {
			get
			{
				return finishKey;
			}
			set
			{
				finishKey = value;
				IsSetFinishKey = true;
			}
		}
		
		public string StartToken {
			get
			{
				return startToken;
			}
			set
			{
				startToken = value;
				IsSetStartToken = true;
			}
		}
		
		public string FinishToken {
			get
			{
				return finishToken;
			}
			set
			{
				finishToken = value;
				IsSetFinishToken = true;
			}
		}
		
		public byte[] StartColumn {
			get
			{
				return startColumn;
			}
			set
			{
				startColumn = value;
				IsSetStartColumn = true;
			}
		}
		
		public byte[] FinishColumn {
			get
			{
				return finishColumn;
			}
			set
			{
				finishColumn = value;
				IsSetFinishColumn = true;
			}
		}
		
		public Dictionary<byte[], byte[]> EqIndexExpression {
			get
			{
				return eqIndexExpression;
			}
			private set
			{
				eqIndexExpression = value;
			}
		}
		
		public Dictionary<byte[], byte[]> GteIndexExpression {
			get
			{
				return gteIndexExpression;
			}
			private set
			{
				gteIndexExpression = value;
			}
		}
		
		public Dictionary<byte[], byte[]> GtIndexExpression {
			get
			{
				return gtIndexExpression;
			}
			private set
			{
				gtIndexExpression = value;
			}
		}
		
		public Dictionary<byte[], byte[]> LteIndexExpression {
			get
			{
				return lteIndexExpression;
			}
			private set
			{
				lteIndexExpression = value;
			}
		}
		
		public Dictionary<byte[], byte[]> LtIndexExpression {
			get
			{
				return ltIndexExpression;
			}
			private set
			{
				ltIndexExpression = value;
			}
		}
		
		public bool Reverse {
			get
			{
				return reverse;
			}
			set
			{
				reverse = value;
				IsSetReverse = true;
			}
		}
		
		public int KeyCount {
			get
			{
				return keyCount;
			}
			set
			{
				keyCount = value;
				IsSetKeyCount = true;
			}
		}
		
		public int Count {
			get
			{
				return count;
			}
			set
			{
				count = value;
				IsSetCount = true;
			}
		}
		
		public ConsistencyLevel ConsistencyLevel 
		{
			get
			{
				return consistencyLevel;
			}
			set
			{
				consistencyLevel = value;
				IsSetConsistencyLevel = true;
			}
		}
		

		public CassandraOperatorCondition ()
		{
		}		
		
		
		public ICassandraOperator<T> CreateOperator()
		{
			if(!IsSetColumnFamily) throw new InvalidOperationException("You should set ColumnFamily.");
			if(IsSetColumn && IsSetColumns) throw new InvalidOperationException("You should set which Column or Columns.");
			if(IsSetKeys)
			{
				if(IsSetStartKey || IsSetFinishKey || IsSetStartToken || IsSetFinishToken || IsSetKeyCount)
				{
					throw new InvalidOperationException("You should set which KeyList or KeyRange.");
				}
			}
			if(IsSetColumns)
			{
				if(IsSetStartColumn || IsSetFinishColumn || IsSetCount || IsSetReverse)
				{
					throw new InvalidOperationException("You should set which ColumnList or SliceRange.");
				}
			}	
			
			SelectOperatorType ();
			
			
			switch (OperatorType) 
			{
				case CassandraOperatorType.Get :
					return GetCassandraOperator<T>.Create(this);
					
				case CassandraOperatorType.GetSlice :
					return GetSliceCassandraOperator<T>.Create(this);
				
				case CassandraOperatorType.MultiGetSlice :
					return MultigetSliceCassandraOperator<T>.Create(this);
					
				case CassandraOperatorType.GetCount :
					return GetCountCassandraOperator<T>.Create(this);
					
				case CassandraOperatorType.MultiGetCount :
					return MultigetCountCassandraOperator<T>.Create(this);
					
				case CassandraOperatorType.GetRangeSlices :
					return GetRangeSlicesCassandraOperator<T>.Create(this);
					
				case CassandraOperatorType.GetIndexedSlices :
					return GetIndexedSlicesCassandraOperator<T>.Create(this);
					
				case CassandraOperatorType.FullTextSearch :
					return FullTextSearchCassandraOperator<T>.Create(this);
					
				case CassandraOperatorType.Insert :
				case CassandraOperatorType.BatchMutate :
				case CassandraOperatorType.Remove :
				case CassandraOperatorType.DescribeKeySpaces :
				case CassandraOperatorType.DescribeClusterName :
				case CassandraOperatorType.DescribeVersion :
				case CassandraOperatorType.DescribeRing :
				case CassandraOperatorType.DescribeKeySpace :
				default:
					throw new InvalidOperationException("It can't be desided on Cassandra operator type.");
			}
		}
		
		private void SelectOperatorType ()
		{
			Type type = typeof(T);
			
			if(OperatorType == CassandraOperatorType.None)
			{
				if(EqIndexExpression.Count  > 0 ||
				   GteIndexExpression.Count > 0 ||
				   GtIndexExpression.Count  > 0 ||
				   LteIndexExpression.Count > 0 ||
				   LtIndexExpression.Count  > 0)
				{
					OperatorType = CassandraOperatorType.GetIndexedSlices;
				}
				else if(IsSetKeys)
				{
					OperatorType = CassandraOperatorType.MultiGetSlice;
				}
				else if(IsSetKey)
				{
					if(type.Equals(typeof(Column)) || 
					   type.Equals(typeof(SuperColumn)) ||
					   type.Equals(typeof(CounterColumn)) ||
					   type.Equals(typeof(CounterSuperColumn)))
					{
						OperatorType = CassandraOperatorType.Get;
					}
					else
					{
						OperatorType = CassandraOperatorType.GetSlice;
					}
				}
				else
				{
					OperatorType = CassandraOperatorType.GetRangeSlices;
				}
			}	
		}
	}
}
